/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package skybash;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import lev.Ln;
import skybash.SkyBashSaveFile.GUISettings;
import skyproc.NPC_.NPCFlag;
import skyproc.NPC_.NPCStat;
import skyproc.NPC_.TINIpackage;
import skyproc.NPC_.TemplateFlag;
import skyproc.*;
import skyproc.NPC_.CSDTpackage;
import skyproc.gui.SPProgressBarPlug;

/**
 *
 * @author pc tech
 */
public class NPCMergerPatch {

    //Lists forms w/ NPC's.
    HashMap<FormID, NPC_> npcList = new HashMap<>();
    //Lists all the base NPC's from the Skyrim.esm, so base data isn't considered changed at any point.
    final HashMap<FormID, NPC_> baseNpcList = new HashMap<>();
    ArrayList<NPC_> deepCopiedList = new ArrayList<>();
    //Grab reference to the mods map.
    HashMap<Mod, ModTags> mods;
    //Storage for modTags.
    ModTags modTags;
    //Full logging boolean for neatness in below code.
    boolean fullLogging;
    //Data locking map.
    HashMap<String, Boolean> dataLock;
    //Storage mod used in counter.
    Mod temp;

    //Constructor for pulling mod tags.
    public NPCMergerPatch(HashMap mods) {
        this.mods = mods;
    }

    public void mergeNPCs() {
        NPC_ newNPC; //Storage NPC used to reference new with old.

        //Set full logging.
        fullLogging = SkyBash.save.getBool(GUISettings.FULL_LOGGING);

        SPProgressBarPlug.progress.setStatus("Processing Base NPCs");
        SPProgressBarPlug.progress.incrementBar();

        for (NPC_ n : SPGlobal.getDB().getMod(0).getNPCs()) {
            //Store the list of base NPC's.
            baseNpcList.put(n.getForm(), n);
        }

        //Iterate through mod lists.
        for (int i = 1; i < SPGlobal.getDB().numMods(); i++) {
            temp = SPGlobal.getDB().getMod(i);
            SPProgressBarPlug.progress.setStatus("Processing NPCs from " + temp.getName());
            SPProgressBarPlug.progress.incrementBar();


            //Get the mod tags for the merger.
            if (mods != null && !mods.isEmpty() && mods.containsKey(temp)) {
                modTags = mods.get(temp);
            } else {
                modTags = new ModTags(true);
            }

            if (!modTags.Deactivate) {
                //For other mods, cross and merge data and add to the global patch.
                for (NPC_ n : temp.getNPCs()) {
                    if (!npcList.containsKey(n.getForm())) {
                        npcList.put(n.getForm(), n);
                    } else {
                        newNPC = npcList.get(n.getForm());

                        //Start the NPC merger.
                        SPGlobal.log("NPC MERGER: ", "Merging NPC " + newNPC.getEDID() + " from " + temp.getName());
                        newNPC = mergeNPCs(newNPC, n);

                        //Add the edited NPC to the map, replacing the old.
                        npcList.put(n.getForm(), newNPC);

                        //Add the NPC to the patch.  If it's edited later, it'll override.
                        SPGlobal.getGlobalPatch().addRecord(newNPC);
                    }
                }
            }
        }
    }

    private NPC_ mergeNPCs(NPC_ n, NPC_ newNPC) {

        //Grabs the base NPC from the list, and copies it below to retain the reference data.
        NPC_ tempNPC;
        if (baseNpcList.containsKey(n.getForm())) {
            tempNPC = baseNpcList.get(n.getForm());
        } else {
            tempNPC = n;
        }

        //Initializes it to the temp NPC.
        NPC_ baseNPC = tempNPC;

        //Deep copies the NPC so the base NPC is retained.

        if (!deepCopiedList.contains(tempNPC)) {
            baseNPC = (NPC_) Ln.deepCopy(tempNPC);
            baseNpcList.put(baseNPC.getForm(), baseNPC);
            deepCopiedList.add(baseNPC);
        }


        //Holder for array lists of items, perks, spells, etc.
        ArrayList tempList;
        ArrayList tempListN;
        ArrayList tempListBase;

        //Holders used with items and perks.
        HashMap<FormID, Integer> baseNPCMap = new HashMap<>();
        HashMap<FormID, Integer> newNPCMap = new HashMap<>();
        HashMap<FormID, Integer> firstNPCMap = new HashMap<>();

        //Integer used to hold a perk number for synchronization.
        int perkNum = 0;

        /*
         * Script merging.
         */
        if (modTags.scripts) {
            tempList = newNPC.scripts.getScripts();
            tempListN = n.scripts.getScripts();
            tempListBase = baseNPC.scripts.getScripts();
            for (int i = 0; i < tempListBase.size(); i++) {
                if (!tempList.contains((ScriptRef) tempListBase.get(i))) {
                    n.scripts.removeScript((ScriptRef) tempListBase.get(i));
                    if (fullLogging) {
                        SPGlobal.log("Scripts: ", "Removed script " + ((ScriptRef) tempListBase.get(i)).getName() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
            for (int i = 0; i < tempList.size(); i++) {
                if (!tempListN.contains((ScriptRef) tempList.get(i)) && !tempListBase.contains((ScriptRef) tempList.get(i))) {
                    n.scripts.addScript((ScriptRef) tempList.get(i));
                    if (fullLogging) {
                        SPGlobal.log("Scripts: ", "Merged script " + ((ScriptRef) tempList.get(i)).getName() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
        }

        /*
         * Keyword merging.
         */
        if (modTags.keywords) {
            tempList = newNPC.keywords.getKeywordRefs();
            tempListN = n.keywords.getKeywordRefs();
            tempListBase = baseNPC.keywords.getKeywordRefs();
            for (int i = 0; i < tempListBase.size(); i++) {
                if (!tempList.contains((FormID) tempListBase.get(i))) {
                    n.keywords.removeKeywordRef((FormID) tempListBase.get(i));
                    if (fullLogging) {
                        SPGlobal.log("Keywords: ", "Removed Keyword " + ((FormID) tempListBase.get(i)) + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
            for (int i = 0; i < tempList.size(); i++) {
                if (!tempListN.contains((FormID) tempList.get(i)) && !tempListBase.contains((FormID) tempList.get(i))) {
                    n.keywords.addKeywordRef((FormID) tempList.get(i));
                    if (fullLogging) {
                        SPGlobal.log("Keywords: ", "Merged Keyword " + tempList.get(i) + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
        }

        /*
         * Stats merging.
         */
        if (modTags.ActorStats) {
            for (NPCStat stat : NPCStat.values()) {
                if (n.get(stat) != newNPC.get(stat) && newNPC.get(stat) != baseNPC.get(stat)) {
                    n.set(stat, newNPC.get(stat));
                    if (fullLogging) {
                        SPGlobal.log("Stats: ", "Merged " + stat.toString() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
        }

        /*
         * Skill level merging (One-Handed Skill, etc).
         */
        if (modTags.ActorSkills) {
            for (Skill skill : Skill.values()) {
                if (n.get(skill) != newNPC.get(skill) && newNPC.get(skill) != baseNPC.get(skill)) {
                    n.set(skill, newNPC.get(skill));
                    if (fullLogging) {
                        SPGlobal.log("Skills: ", "Merged " + skill.toString() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
        }

        /*
         * Skill mod merging, same as above but with mods.
         */
        if (modTags.ActorSkills) {
            for (Skill skill : Skill.values()) {
                if (n.getMod(skill) != newNPC.getMod(skill) && newNPC.getMod(skill) != baseNPC.getMod(skill)) {
                    n.setMod(skill, newNPC.getMod(skill));
                    if (fullLogging) {
                        SPGlobal.log("Skill Mods: ", "Merged " + skill.toString() + " mod to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());

                    }
                }
            }
        }

        /*
         * Flag merging (unique, persistent, etc).
         */
        if (modTags.ActorFlags) {
            for (NPCFlag flag : NPCFlag.values()) {
                if (n.get(flag) != newNPC.get(flag) && newNPC.get(flag) != baseNPC.get(flag)) {
                    n.set(flag, newNPC.get(flag));
                    if (fullLogging) {
                        SPGlobal.log("Flags: ", "Merged " + flag.toString() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
        }

        /*
         * Template flag merging (what it inherits from a template).
         */
        if (modTags.ActorTemplateFlags) {
            for (TemplateFlag flag : TemplateFlag.values()) {
                if (n.get(flag) != newNPC.get(flag) && newNPC.get(flag) != baseNPC.get(flag)) {
                    n.set(flag, newNPC.get(flag));
                    if (fullLogging) {
                        SPGlobal.log("Template Flags: ", "Merged " + flag.toString() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
        }

        /*
         * AI Package list merging.
         */
        if (modTags.ActorAIPackages) {
            tempList = newNPC.getAIPackages();
            tempListN = n.getAIPackages();
            tempListBase = baseNPC.getAIPackages();
            for (int i = 0; i < tempListBase.size(); i++) {
                if (!tempList.contains((FormID) tempListBase.get(i))) {
                    n.removeAIPackage((FormID) tempListBase.get(i));
                    if (fullLogging) {
                        SPGlobal.log("AI Packages: ", "Removed AI Package " + ((FormID) tempListBase.get(i)) + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
            for (int i = 0; i < tempList.size(); i++) {
                if (!tempListN.contains((FormID) tempList.get(i)) && !tempListBase.contains((FormID) tempList.get(i))) {
                    n.addAIPackage((FormID) tempList.get(i));
                    if (fullLogging) {
                        SPGlobal.log("AI Packages: ", "Merged AI Package " + (FormID) tempList.get(i) + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
        }

        /*
         * Spell list merging.
         */
        if (modTags.ActorSpells) {
            tempList = newNPC.getSpells();
            tempListN = n.getSpells();
            tempListBase = baseNPC.getSpells();
            for (int i = 0; i < tempListBase.size(); i++) {
                if (!tempList.contains((FormID) tempListBase.get(i))) {
                    n.removeSpell((FormID) tempListBase.get(i));
                    if (fullLogging) {
                        SPGlobal.log("Spells: ", "Removed Spell " + ((FormID) tempListBase.get(i)) + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
            for (int i = 0; i < tempList.size(); i++) {
                if (!tempListN.contains((FormID) tempList.get(i)) && !tempListBase.contains((FormID) tempList.get(i))) {
                    n.addSpell((FormID) tempList.get(i));
                    if (fullLogging) {
                        SPGlobal.log("Spells: ", "Merged Spell " + (FormID) tempList.get(i) + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
        }

        /*
         * Sound package merging.
         */
        if (modTags.Sounds) {
            n.clearSounds();
            tempList = newNPC.getSounds();
            for (int i = 0; i < tempList.size(); i++) {
                n.addSound((CSDTpackage) tempList.get(i));
                if (fullLogging) {
                    SPGlobal.log("Sounds: ", "Merged Sound package (record not available in readable string form) to "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Tint package merging.
         */
        if (modTags.ActorFaces) {
            tempList = newNPC.getTinting();
            n.clearTinting();
            for (int i = 0; i < tempList.size(); i++) {
                n.addTinting((TINIpackage) tempList.get(i));
                if (fullLogging) {
                    SPGlobal.log("Faces: ", "Merged Tint Package (record not available in readable string form) to "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Face morph merging.
         */
        if (modTags.ActorFaces) {
            if (n.getNAM9() != newNPC.getNAM9() && newNPC.getNAM9() != baseNPC.getNAM9()) {
                n.setNAM9(newNPC.getNAM9());
                if (fullLogging) {
                    SPGlobal.log("Faces: ", "Merged Face Morphs (record not available in readable string form) for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Face parts merging.
         */
        if (modTags.ActorFaces) {
            if (n.getNAMA() != newNPC.getNAMA() && newNPC.getNAMA() != baseNPC.getNAMA()) {
                n.setNAMA(newNPC.getNAMA());
                if (fullLogging) {
                    SPGlobal.log("Faces: ", "Merged Face Parts (record not available in readable string form) for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Texture Lighting merging.
         */
        if (modTags.ActorFaces) {
            if (n.getQNAM() != newNPC.getQNAM() && newNPC.getQNAM() != baseNPC.getQNAM()) {
                n.setQNAM(newNPC.getQNAM());
                if (fullLogging) {
                    SPGlobal.log("Faces: ", "Merged Texture Lighting (record not available in readable string form) for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * PNAM merging.
         */
        if (modTags.Sounds) {
            n.clearPNAMs();
            tempList = newNPC.getPNAMs();
            for (int i = 0; i < tempList.size(); i++) {
                n.addPNAM((FormID) tempList.get(i));
                if (fullLogging) {
                    SPGlobal.log("Sounds: ", "Merged PNAM " + ((FormID) tempList.get(i)).getFormStr() + " to "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Perk merging w/ SubFormInts.
         */
        if (modTags.ActorPerks) {
            tempList = newNPC.getPerks();  //Stores perks from the second NPC.
            for (int i = 0; i < tempList.size(); i++) {
                newNPCMap.put(((SubFormInt) tempList.get(i)).getForm(), ((SubFormInt) tempList.get(i)).getNum());
            }
            tempList = n.getPerks();  // Stores perks from first NPC and uses below for the num to synchronize the integers.
            for (int i = 0; i < tempList.size(); i++) {
                firstNPCMap.put(((SubFormInt) tempList.get(i)).getForm(), ((SubFormInt) tempList.get(i)).getNum());
            }
            tempList = baseNPC.getPerks();  // Stores perks from first NPC and uses below for the num to synchronize the integers.
            for (int i = 0; i < tempList.size(); i++) {
                baseNPCMap.put(((SubFormInt) tempList.get(i)).getForm(), ((SubFormInt) tempList.get(i)).getNum());
            }

            //Just a method to grab any one of the perk numbers.
            for (Map.Entry<FormID, Integer> entry : newNPCMap.entrySet()) {
                try {
                    perkNum = entry.getValue();
                } catch (NullPointerException e) {
                    perkNum = 0;
                }
                break;
            }

            for (Map.Entry<FormID, Integer> entry : baseNPCMap.entrySet()) {
                if (!newNPCMap.containsKey(entry.getKey())) {
                    n.removePerk(entry.getKey());
                    if (fullLogging) {
                        SPGlobal.log("Perks: ", "Removed Perk " + entry.getKey() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }
            for (Map.Entry<FormID, Integer> entry : newNPCMap.entrySet()) {
                if (!firstNPCMap.containsKey(entry.getKey()) && !baseNPCMap.containsKey(entry.getKey())) {
                    n.addPerk(entry.getKey(), perkNum);
                    if (fullLogging) {
                        SPGlobal.log("Perks: ", "Merged Perk " + entry.getKey() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }

            newNPCMap.clear();
            baseNPCMap.clear();
            firstNPCMap.clear();
        }

        /*
         * Faction merging.
         */
        if (modTags.ActorFactions) {
            tempList = n.getFactions(); //Stores the first NPC's items, Forms below.
            for (int i = 0; i < tempList.size(); i++) {
                firstNPCMap.put(((SubFormInt) tempList.get(i)).getForm(), ((SubFormInt) tempList.get(i)).getNum());
            }
            tempList = baseNPC.getFactions(); //Stores the base NPC's item counts below to make sure item counts aren't reset to base.
            for (int i = 0; i < tempList.size(); i++) {
                baseNPCMap.put(((SubFormInt) tempList.get(i)).getForm(), ((SubFormInt) tempList.get(i)).getNum());
            }
            tempList = newNPC.getFactions(); //Stores the second and uses it below, reversed from perks (using the second num).
            for (int i = 0; i < tempList.size(); i++) {
                newNPCMap.put(((SubFormInt) tempList.get(i)).getForm(), ((SubFormInt) tempList.get(i)).getNum());
            }

            for (Map.Entry<FormID, Integer> entry : baseNPCMap.entrySet()) {
                if (!newNPCMap.containsKey(entry.getKey())) {
                    n.removeFaction(entry.getKey());
                    if (fullLogging) {
                        SPGlobal.log("Factions: ", "Removed Faction " + entry.getKey() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }

            for (Map.Entry<FormID, Integer> entry : newNPCMap.entrySet()) {
                if (firstNPCMap.containsKey(entry.getKey())) {
                    if (baseNPCMap.get(entry.getKey()) != null) {
                        if (entry.getValue() != baseNPCMap.get(entry.getKey())) {
                            n.removeFaction(entry.getKey());
                            n.addFaction(entry.getKey(), entry.getValue());
                            if (fullLogging) {
                                SPGlobal.log("Factions: ", "Merged Faction Rank for item " + entry.getKey() + " to "
                                        + newNPC.getEDID() + " from Mod " + temp.getName()
                                        + " to rank " + entry.getValue());
                            }
                        }
                    } else if (firstNPCMap.get(entry.getKey()) != entry.getValue()) {
                        n.removeFaction(entry.getKey());
                        n.addFaction(entry.getKey(), entry.getValue());
                        if (fullLogging) {
                            SPGlobal.log("Factions: ", "Merged Faction Rank for item " + entry.getKey() + " to "
                                    + newNPC.getEDID() + " from Mod " + temp.getName()
                                    + " to rank " + entry.getValue());
                        }
                    }
                } else if (!baseNPCMap.containsKey(entry.getKey())) {
                    n.addFaction(entry.getKey(), entry.getValue());
                    if (fullLogging) {
                        SPGlobal.log("Factions: ", "Added Faction " + entry.getKey() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName()
                                + " with rank " + entry.getValue());
                    }
                } else {
                    n.removeFaction(entry.getKey());
                    n.addFaction(entry.getKey(), entry.getValue());
                    if (fullLogging) {
                        SPGlobal.log("Factions: ", "Merged Faction Rank for item " + entry.getKey() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName()
                                + " to rank " + entry.getValue());
                    }
                }
            }
            newNPCMap.clear();
            firstNPCMap.clear();
            baseNPCMap.clear();
        }

        /*
         * Item merging w/ SubFormInts.
         */

        if (modTags.ActorInventories) {
            tempList = n.getItems(); //Stores the first NPC's items, Forms below.
            for (int i = 0; i < tempList.size(); i++) {
                firstNPCMap.put(((SubFormInt) tempList.get(i)).getForm(), ((SubFormInt) tempList.get(i)).getNum());
            }
            tempList = baseNPC.getItems(); //Stores the base NPC's item counts below to make sure item counts aren't reset to base.
            for (int i = 0; i < tempList.size(); i++) {
                baseNPCMap.put(((SubFormInt) tempList.get(i)).getForm(), ((SubFormInt) tempList.get(i)).getNum());
            }
            tempList = newNPC.getItems(); //Stores the second and uses it below, reversed from perks (using the second num).
            for (int i = 0; i < tempList.size(); i++) {
                newNPCMap.put(((SubFormInt) tempList.get(i)).getForm(), ((SubFormInt) tempList.get(i)).getNum());
            }

            for (Map.Entry<FormID, Integer> entry : baseNPCMap.entrySet()) {
                if (!newNPCMap.containsKey(entry.getKey())) {
                    n.removeItem(entry.getKey());
                    if (fullLogging) {
                        SPGlobal.log("Inventories: ", "Removed Item " + entry.getKey() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName());
                    }
                }
            }

            for (Map.Entry<FormID, Integer> entry : newNPCMap.entrySet()) {
                if (firstNPCMap.containsKey(entry.getKey())) {
                    if (baseNPCMap.get(entry.getKey()) != null) {
                        if (entry.getValue() != baseNPCMap.get(entry.getKey())) {
                            n.removeItem(entry.getKey());
                            n.addItem(entry.getKey(), entry.getValue());
                            if (fullLogging) {
                                SPGlobal.log("Inventories: ", "Merged Item Count/Level for item " + entry.getKey() + " to "
                                        + newNPC.getEDID() + " from Mod " + temp.getName()
                                        + " to count " + entry.getValue());
                            }
                        }
                    } else if (firstNPCMap.get(entry.getKey()) != entry.getValue()) {
                        n.removeItem(entry.getKey());
                        n.addItem(entry.getKey(), entry.getValue());
                        if (fullLogging) {
                            SPGlobal.log("Inventories: ", "Merged Item Count/Level for item " + entry.getKey() + " to "
                                    + newNPC.getEDID() + " from Mod " + temp.getName()
                                    + " to count " + entry.getValue());
                        }
                    }
                } else if (!baseNPCMap.containsKey(entry.getKey())) {
                    n.addItem(entry.getKey(), entry.getValue());
                    if (fullLogging) {
                        SPGlobal.log("Inventories: ", "Added Item " + entry.getKey() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName()
                                + " with count " + entry.getValue());
                    }
                } else {
                    n.removeItem(entry.getKey());
                    n.addItem(entry.getKey(), entry.getValue());
                    if (fullLogging) {
                        SPGlobal.log("Inventories: ", "Merged Item Count/Level for item " + entry.getKey() + " to "
                                + newNPC.getEDID() + " from Mod " + temp.getName()
                                + " to count " + entry.getValue());
                    }
                }
            }
            newNPCMap.clear();
            firstNPCMap.clear();
            baseNPCMap.clear();
        }

        /*
         * Aggression levels.
         */
        if (modTags.ActorAIDT) {
            if (n.getAggression() != newNPC.getAggression() && newNPC.getAggression() != baseNPC.getAggression()) {
                n.setAggression(newNPC.getAggression());
                if (fullLogging) {
                    SPGlobal.log("AIDT: ", "Merged Aggression to " + n.getAggression().toString() + " to "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Aggro distances.
         */
        if (modTags.ActorAIDT) {
            if (n.getAggroAttack() != newNPC.getAggroAttack() && newNPC.getAggroAttack() != baseNPC.getAggroAttack()) {
                n.setAggroAttack(newNPC.getAggroAttack());
                if (fullLogging) {
                    SPGlobal.log("AIDT: ", "Merged Aggro Attack Range to " + n.getAggroAttack() + " to "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        if (modTags.ActorAIDT) {
            if (n.getAggroWarn() != newNPC.getAggroWarn() && newNPC.getAggroWarn() != baseNPC.getAggroWarn()) {
                n.setAggroWarn(newNPC.getAggroWarn());
                if (fullLogging) {
                    SPGlobal.log("AIDT: ", "Merged Aggro Warn Range to " + n.getAggroWarn() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        if (modTags.ActorAIDT) {
            if (n.getAggroWarnAttack() != newNPC.getAggroWarnAttack() && newNPC.getAggroWarnAttack() != baseNPC.getAggroWarnAttack()) {
                n.setAggroWarnAttack(newNPC.getAggroWarnAttack());
                if (fullLogging) {
                    SPGlobal.log("AIDT: ", "Merged Aggro Warn Attack Range to " + n.getAggroWarnAttack() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Assistance flags.
         */
        if (modTags.ActorAIDT) {
            if (n.getAssistance() != newNPC.getAssistance() && newNPC.getAssistance() != baseNPC.getAssistance()) {
                n.setAssistance(newNPC.getAssistance());
                if (fullLogging) {
                    SPGlobal.log("AIDT: ", "Merged Assistance flag to " + n.getAssistance().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Race attack data for animations.
         */
        if (modTags.ActorAnimations) {
            if (n.getAttackDataRace() != newNPC.getAttackDataRace() && newNPC.getAttackDataRace() != baseNPC.getAttackDataRace()) {
                n.setAttackDataRace(newNPC.getAttackDataRace());
                if (fullLogging) {
                    SPGlobal.log("Animations: ", "Merged Attack Data Race " + n.getAttackDataRace().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Audio template to use.
         */
        if (modTags.Sounds) {
            if (n.getAudioTemplate() != newNPC.getAudioTemplate() && newNPC.getAudioTemplate() != baseNPC.getAudioTemplate()) {
                n.setAudioTemplate(newNPC.getAudioTemplate());
                if (fullLogging) {
                    SPGlobal.log("AudioTemplate: ", "Merged Audio Template to " + n.getAudioTemplate().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Combat override AI package.
         */
        if (modTags.ActorAIPackages) {
            if (n.getCombatOverride() != newNPC.getCombatOverride() && newNPC.getCombatOverride() != baseNPC.getCombatOverride()) {
                n.setCombatOverride(newNPC.getCombatOverride());
                if (fullLogging) {
                    SPGlobal.log("AIPackages: ", "Merged Combat Override Package to " + n.getCombatOverride().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Combat style for NPC.
         */
        if (modTags.ActorCombatStyle) {
            if (n.getCombatStyle() != newNPC.getCombatStyle() && newNPC.getCombatStyle() != baseNPC.getCombatStyle()) {
                n.setCombatStyle(newNPC.getCombatStyle());
                if (fullLogging) {
                    SPGlobal.log("Combat Style: ", "Merged Combat Style to " + n.getCombatStyle().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Confidence level.
         */
        if (modTags.ActorAIDT) {
            if (n.getConfidence() != newNPC.getConfidence() && newNPC.getConfidence() != baseNPC.getConfidence()) {
                n.setConfidence(newNPC.getConfidence());
                if (fullLogging) {
                    SPGlobal.log("AIDT: ", "Merged Confidence to " + n.getConfidence().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Crime faction.
         */
        if (modTags.ActorFactions) {
            if (n.getCrimeFaction() != newNPC.getCrimeFaction() && newNPC.getCrimeFaction() != baseNPC.getCrimeFaction()) {
                n.setCrimeFaction(newNPC.getCrimeFaction());
                if (fullLogging) {
                    SPGlobal.log("Crime Faction: ", "Merged Crime Faction to " + n.getCrimeFaction().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Item to drop on death.
         */
        if (modTags.ActorDeathItem) {
            if (n.getDeathItem() != newNPC.getDeathItem() && newNPC.getDeathItem() != baseNPC.getDeathItem()) {
                n.setDeathItem(newNPC.getDeathItem());
                if (fullLogging) {
                    SPGlobal.log("Death Item: ", "Merged Death Item to " + n.getDeathItem().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Default outfit for an NPC.
         */
        if (modTags.ActorOutfits) {
            if (n.getDefaultOutfit() != newNPC.getDefaultOutfit() && newNPC.getDefaultOutfit() != baseNPC.getDefaultOutfit()) {
                n.setDefaultOutfit(newNPC.getDefaultOutfit());
                if (fullLogging) {
                    SPGlobal.log("Default Outfit: ", "Merged Default Outfit to " + n.getDefaultOutfit().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Default AI package list.
         */
        if (modTags.ActorAIPackages) {
            if (n.getDefaultPackageList() != newNPC.getDefaultPackageList() && newNPC.getDefaultPackageList() != baseNPC.getDefaultPackageList()) {
                n.setDefaultPackageList(newNPC.getDefaultPackageList());
                if (fullLogging) {
                    SPGlobal.log("AIPackages: ", "Merged Default Package List to " + n.getDefaultPackageList().toString()
                            + " for " + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Energy level for NPC.
         */
        if (modTags.ActorACBS) {
            if (n.getEnergy() != newNPC.getEnergy() && newNPC.getEnergy() != baseNPC.getEnergy()) {
                n.setEnergy(newNPC.getEnergy());
                if (fullLogging) {
                    SPGlobal.log("ACBS: ", "Merged Energy to " + n.getEnergy() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Distance that LOD model is shown.
         */
        if (modTags.Graphics) {
            if (n.getFarAwayModelDistance() != newNPC.getFarAwayModelDistance() && newNPC.getFarAwayModelDistance() != baseNPC.getFarAwayModelDistance()) {
                n.setFarAwayModelDistance(newNPC.getFarAwayModelDistance());
                if (fullLogging) {
                    SPGlobal.log("Graphics: ", "Merged LOD Model Distance to " + n.getFarAwayModelDistance() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * LOD model to use.
         */
        if (modTags.Graphics) {
            if (n.getFarAwayModelSkin() != newNPC.getFarAwayModelSkin() && newNPC.getFarAwayModelSkin() != baseNPC.getFarAwayModelSkin()) {
                n.setFarAwayModelSkin(newNPC.getFarAwayModelSkin());
                if (fullLogging) {
                    SPGlobal.log("Graphics: ", "Merged LOD Model to " + n.getFarAwayModelSkin().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Offset for fatigue from level (how different it is from standard
         * auto-calc for the level).
         */
        if (modTags.ActorACBS) {
            if (n.getFatigueOffset() != newNPC.getFatigueOffset() && newNPC.getFatigueOffset() != baseNPC.getFatigueOffset()) {
                n.setFatigueOffset(newNPC.getFatigueOffset());
                if (fullLogging) {
                    SPGlobal.log("ACBS: ", "Merged Fatigue Offset to " + n.getFatigueOffset() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Head textures.
         */

        if (modTags.Graphics || modTags.ActorFaces) {
            if (n.getFeatureSet() != newNPC.getFeatureSet() && newNPC.getFeatureSet() != baseNPC.getFeatureSet()) {
                n.setFeatureSet(newNPC.getFeatureSet());
                if (fullLogging) {
                    SPGlobal.log("Faces: ", "Merged Face to " + n.getFeatureSet().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Not really sure?
         */
        if (n.getGearedUpWeapons() != newNPC.getGearedUpWeapons() && newNPC.getGearedUpWeapons() != baseNPC.getGearedUpWeapons()) {
            n.setGearedUpWeapons(newNPC.getGearedUpWeapons());
            if (fullLogging) {
                SPGlobal.log("Other: ", "Merged Geared Up Weapons (?) to " + n.getGearedUpWeapons() + " for "
                        + newNPC.getEDID() + " from Mod " + temp.getName());
            }
        }

        /*
         * Not used in-game.
         */
        if (n.getGiftFilter() != newNPC.getGiftFilter() && newNPC.getGiftFilter() != baseNPC.getGiftFilter()) {
            n.setGiftFilter(newNPC.getGiftFilter());
            if (fullLogging) {
                SPGlobal.log("Other: ", "Merged Gift Filter to " + n.getGiftFilter().toString() + " for "
                        + newNPC.getEDID() + " from Mod " + temp.getName());
            }
        }

        /*
         * Guard worn override. Not sure 'exactly' what it does.
         */
        if (modTags.ActorOutfits) {
            if (n.getGuardWornOverride() != newNPC.getGuardWornOverride() && newNPC.getGuardWornOverride() != baseNPC.getGuardWornOverride()) {
                n.setGuardWornOverride(newNPC.getGuardWornOverride());
                if (fullLogging) {
                    SPGlobal.log("Other: ", "Merged Guard Worn Override (?) to " + n.getGuardWornOverride().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Hair color.
         */
        if (modTags.ActorHairOnly) {
            if (n.getHairColor() != newNPC.getHairColor() && newNPC.getHairColor() != baseNPC.getHairColor()) {
                n.setHairColor(newNPC.getHairColor());
                if (fullLogging) {
                    SPGlobal.log("Hair: ", "Merged Hair Color to " + n.getHairColor().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Health offset from normal for level.
         */
        if (modTags.ActorACBS) {
            if (n.getHealthOffset() != newNPC.getHealthOffset() && newNPC.getHealthOffset() != baseNPC.getHealthOffset()) {
                n.setHealthOffset(newNPC.getHealthOffset());
                if (fullLogging) {
                    SPGlobal.log("ACBS: ", "Merged Health Offset to " + n.getHealthOffset() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Height for NPC.
         */
        if (modTags.ActorStats) {
            if (n.getHeight() != newNPC.getHeight() && newNPC.getHeight() != baseNPC.getHeight()) {
                n.setHeight(newNPC.getHeight());
                if (fullLogging) {
                    SPGlobal.log("Height: ", "Merged Height to " + n.getHeight() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Magicka offset from normal for level.
         */
        if (modTags.ActorACBS) {
            if (n.getMagickaOffset() != newNPC.getMagickaOffset() && newNPC.getMagickaOffset() != baseNPC.getMagickaOffset()) {
                n.setMagickaOffset(newNPC.getMagickaOffset());
                if (fullLogging) {
                    SPGlobal.log("ACBS: ", "Merged Aggresion to " + n.getMagickaOffset() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Mood of NPC.
         */
        if (modTags.ActorAIDT) {
            if (n.getMood() != newNPC.getMood() && newNPC.getMood() != baseNPC.getMood()) {
                n.setMood(newNPC.getMood());
                if (fullLogging) {
                    SPGlobal.log("AIDT: ", "Merged Mood to " + n.getMood().toString() + " from "
                            + newNPC.getAggression().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * NPC class.
         */
        if (modTags.ActorClass) {
            if (n.getNPCClass() != newNPC.getNPCClass() && newNPC.getNPCClass() != baseNPC.getNPCClass()) {
                n.setNPCClass(newNPC.getNPCClass());
                if (fullLogging) {
                    SPGlobal.log("Class: ", "Merged Class to " + n.getNPCClass().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Morality of NPC.
         */
        if (modTags.ActorAIDT) {
            if (n.getMorality() != newNPC.getMorality() && newNPC.getMorality() != baseNPC.getMorality()) {
                n.setMorality(newNPC.getMorality());
                if (fullLogging) {
                    SPGlobal.log("AIDT: ", "Merged Morality to " + n.getMorality().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * NPC name.
         */
        if (modTags.names) {
            if (!n.getName().equals(newNPC.getName()) && !newNPC.getName().equals(baseNPC.getName())) {
                n.setName(newNPC.getName());
                if (fullLogging) {
                    SPGlobal.log("Names: ", "Merged name to " + n.getName() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Observe dead override - override package to observe a dead person?
         */
        if (modTags.ActorAIPackages) {
            if (n.getObserveDeadOverride() != newNPC.getObserveDeadOverride() && newNPC.getObserveDeadOverride() != baseNPC.getObserveDeadOverride()) {
                n.setObserveDeadOverride(newNPC.getObserveDeadOverride());
                if (fullLogging) {
                    SPGlobal.log("AI Packages: ", "Merged Observe Dead Override to "
                            + n.getObserveDeadOverride().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * NPC race.
         */
        if (modTags.ActorRace) {
            if (n.getRace() != newNPC.getRace() && newNPC.getRace() != baseNPC.getRace()) {
                n.setRace(newNPC.getRace());
                if (fullLogging) {
                    SPGlobal.log("Race: ", "Merged Race to " + n.getRace().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Short name, used in some circumstances (subtitles, I think?).
         */
        if (modTags.names) {
            if (!n.getShortName().equals(newNPC.getShortName()) && !newNPC.getShortName().equals(baseNPC.getShortName())) {
                n.setShortName(newNPC.getShortName());
                if (fullLogging) {
                    SPGlobal.log("Names: ", "Merged Short Name to " + n.getShortName().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Skin for NPC.
         */
        if (modTags.ActorSkin) {
            if (n.getSkin() != newNPC.getSkin() && newNPC.getSkin() != baseNPC.getSkin()) {
                n.setSkin(newNPC.getSkin());
                if (fullLogging) {
                    SPGlobal.log("Skins: ", "Merged Skin to " + n.getSkin().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Default sleeping outfit.
         */
        if (modTags.ActorOutfits) {
            if (n.getSleepingOutfit() != newNPC.getSleepingOutfit() && newNPC.getSleepingOutfit() != baseNPC.getSleepingOutfit()) {
                n.setSleepingOutfit(newNPC.getSleepingOutfit());
                if (fullLogging) {
                    SPGlobal.log("Sleeping Outfit: ", "Merged Sleeping Outfit to " + n.getSleepingOutfit().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Sound volume for NPC.
         */
        if (modTags.Sounds) {
            if (n.getSoundVolume() != newNPC.getSoundVolume() && newNPC.getSoundVolume() != baseNPC.getSoundVolume()) {
                n.setSoundVolume(newNPC.getSoundVolume());
                if (fullLogging) {
                    SPGlobal.log("Sound: ", "Merged Aggresion to " + n.getSoundVolume().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Specator override AI package?
         */
        if (modTags.ActorAIPackages) {
            if (n.getSpectatorOverride() != newNPC.getSpectatorOverride() && newNPC.getSpectatorOverride() != baseNPC.getSpectatorOverride()) {
                n.setSpectatorOverride(newNPC.getSpectatorOverride());
                if (fullLogging) {
                    SPGlobal.log("AIPackages: ", "Merged Spectator Override Package to " + n.getSpectatorOverride().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * NPC template (for templating to another NPC and pulling stats, or a
         * leveled list).
         */
        if (modTags.ActorTemplates) {
            if (n.getTemplate() != newNPC.getTemplate() && newNPC.getTemplate() != baseNPC.getTemplate()) {
                n.setTemplate(newNPC.getTemplate());
                if (fullLogging) {
                    SPGlobal.log("Template: ", "Merged Template to " + n.getTemplate().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Voice type for NPC.
         */
        if (modTags.ActorVoice) {
            if (n.getVoiceType() != newNPC.getVoiceType() && newNPC.getVoiceType() != baseNPC.getVoiceType()) {
                n.setVoiceType(newNPC.getVoiceType());
                if (fullLogging) {
                    SPGlobal.log("Voice: ", "Merged Voice to " + n.getVoiceType().toString() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        /*
         * Weight for NPC.
         */
        if (modTags.ActorStats) {
            if (n.getWeight() != newNPC.getWeight() && newNPC.getWeight() != baseNPC.getWeight()) {
                n.setWeight(newNPC.getWeight());
                if (fullLogging) {
                    SPGlobal.log("Weight: ", "Merged Weight to " + n.getWeight() + " for "
                            + newNPC.getEDID() + " from Mod " + temp.getName());
                }
            }
        }

        return n;


    }
}